package frpc.boot.centre;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import frpc.manager.operation.IRedisManage;
import frpc.manager.operation.RedisManagerImpl;
import org.redisson.Redisson;
import org.redisson.config.Config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;

/**
 * 类描述:  Redis配置处理中心
 *
 * @author 王雷
 * @version 1.0.0
 * @date 2021/4/16 0016 上午 11:00
 */
public class RedisConfigManagerCentre {

    @PostConstruct
    public void init() {
    }

    @Bean
    public IRedisManage iRedisManage() {
        return new InteriorRedisManager();
    }

    /**
     * 类描述: Redis业务处理的内部实现类
     * <p/>
     * 基于设计原则，避免外部修改
     *
     * @author : 王雷
     * @date : 2020/11/26 0026 下午 5:33
     */
    private static class InteriorRedisManager extends RedisManagerImpl {
    }

    /**
     * 功能描述：注入Redis操作RedisTemplate对象
     *
     * @author : 王雷
     * @date : 2021/4/16 0016 上午 11:30
     */
    @Bean
    @ConditionalOnMissingBean(RedisTemplate.class)
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {

        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);

        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //解决jackson2无法反序列化LocalDateTime的问题
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        mapper.registerModule(new JavaTimeModule());

        /*
         * mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
         * 过时方法处理
         * */
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL
                , JsonTypeInfo.As.WRAPPER_ARRAY);
        serializer.setObjectMapper(mapper);

        //String序列化配置
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        //key和hash的key都采用String的序列化配置
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);

        template.setValueSerializer(serializer);
        //使用StringRedisSerializer来序列化和反序列化redis的key值
        /*template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());*/
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        //开启事务支持
//        template.setEnableTransactionSupport(true);
        return template;
    }

    /**
     * 功能描述：配置Redisson对象
     *
     * @author : 王雷
     * @date : 2019/10/22 0022 下午 6:01
     */
    @Bean
    public Redisson redisson(RedisProperties redisProperties) {
        String qz = "redis://";
        Config config = new Config();

        /*config.useSingleServer().setConnectionPoolSize(500)//设置对于master节点的连接池中连接数最大为500
        config.useSingleServer().setIdleConnectionTimeout(10000)//如果当前连接池里的连接数量超过了最小空闲连接数，而同时有连接空闲时间超过了该数值，那么这些连接将会自动被关闭，并从连接池里去掉。时间单位是毫秒。
        config.useSingleServer().setConnectTimeout(30000)//同任何节点建立连接时的等待超时。时间单位是毫秒。
        config.useSingleServer().setTimeout(3000)//等待节点回复命令的时间。该时间从命令发送成功时开始计时。
        config.useSingleServer().setPingTimeout(30000)
        config.useSingleServer().setReconnectionTimeout(3000)//当与某个节点的连接断开时，等待与其重新建立连接的时间间隔。时间单位是毫秒。*/

        final List<String> nodes = new ArrayList<>(16);
        if (redisProperties.getCluster() != null) {
            nodes.addAll(redisProperties.getCluster().getNodes());
        }

        if (nodes.size() > 0) {
            if (redisProperties.getPassword() != null && !"".equals(redisProperties.getPassword().trim())) {
                config.useClusterServers().setPassword(redisProperties.getPassword());
            }
            //TODO 集群配置
            nodes.forEach(url -> {
                config.useClusterServers().addNodeAddress(qz + url);
            });
        } else {
            if (redisProperties.getPassword() != null && !"".equals(redisProperties.getPassword().trim())) {
                config.useSingleServer().setPassword(redisProperties.getPassword());
            }
            //TODO 单机配置
            config.useSingleServer().setAddress(qz + redisProperties.getHost() + ":" + redisProperties.getPort()).setDatabase(redisProperties.getDatabase());
        }
        return (Redisson) Redisson.create(config);
    }
}
